Rails + OpenAPI + ViewComponent + Hotwire
JSON Placeholder APIのOpenAPI化
code:txt
OpenAPIのYAMLファイルはconfig/api_docs配下に配置している。
OpenAPIファイルからRuby用のクライアントライブラリの生成
インストールはbrewで行った。
code:sh
brew install openapi-generator
下記のようなコマンドを実行すると指定したdir配下にGemが生成される。
code:sh
openapi-generator generate -i ./config/api_docs/openapi.yml -g ruby -o ./tmp/out
あとはGemfileに追記してローカルinstallする。
code:Gemfile
gem 'openapi_client', path: './tmp/out'
実行に関しては./tmp/out/README.mdに使い方が書いてあるのでそれを参照する。
code:rb
require 'openapi_client'
api_instance = OpenapiClient::DefaultApi.new
opts = {
user_id: 1
}
begin
result = api_instance.posts_get(opts)
p result
rescue OpenapiClient::ApiError => e
puts "Exception when calling DefaultApi->posts_get: #{e}" end
Postの表示系を実装
routes.rb/controller/viewsに適切にファイルを生成していくだけ。controllerでは先ほど生成したOpenapiClientライブラリを使っている。
code:rb
class PostsController < ApplicationController
def index
client = OpenapiClient::DefaultApi.new
@posts = client.posts_get
end
def show
client = OpenapiClient::DefaultApi.new
@post = client.posts_id_get(params:id) end
end
ViewComponentの導入
code:Gemfile
gem 'view_component'
code:sh
bundle exec rails g component Post post
Postの更新系を実装
特筆することはない。唯一turboでdeleteするときにlinkでdeleteができないところは注意が必要。turboが効いてる場合は以下のようにdata-turbo-methodとdata-turbo-confirmを指定すると良い。
code:html
<a href="/posts/<%= @post.id %>" data-turbo-method="delete" data-turbo-confirm="Are you sure?">Delete</a>
Tailwindのコンポーネント
notyfを導入する
importmapでnpmパッケージを導入するにはまずpinする。これでnotyfがconfig/importmaprbに追加される。
code:sh
bundle exec bin/importmap pin notyf
notyf用のCSSを追加する。views/layouts/application.html.erbに<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/notyf@3/notyf.min.css">を追加。CSSに関してはimportmapでいい感じにやってくれないので本家ライブラリのCSSをDLしてきてassets配下に置くかcdnから読み込むしかない。だるい。
notyf用のViewComponentを作成する
code:sh
bundle exec rails g component Notyf
app/components/notyf_component.rb
code:rb
class NotyfComponent < ViewComponent::Base
def initialize(flash = nil)
@flash = flash
super
end
end
app/components/notyf_component.html.erb
code:erb
<% if flash.present? %>
<div data-controller="notyf"
data-notyf-flash-value='<%= flash.as_json %>'>
</div>
<% end %>
app/javascript/controllers/notyf_controller.js
code:js
import { Controller } from '@hotwired/stimulus'
import * as Notyf from 'notyf'
export default class extends Controller {
static values = {
flash: Array
}
connect() {
const notyf = new Notyf.Notyf()
this.flashValue.forEach((flash) => {
case 'notice':
break
case 'alert':
break
}
})
}
}
viewの方に<%= render NotyfComponent.new(flash) %>を埋め込んで完了。